#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    scene = new QGraphicsScene(this);
    game = new GameArea;
    client = new QTcpSocket(this);

    scene->addItem(game);

    ui->graphicsView->setScene(scene);

    blockSize = 0;

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::updatePoints()
{
    ui->listWidget->clear();
    QList<QPair<quint32, quint32 > > points = game->getPointOfPlayers();

    QList<QPair<quint32, quint32 > >::iterator cur = points.begin();
    while(cur != points.end())
    {
        QString itm;
        if(cur->first == myID)
            itm+="My score";
        else
            itm+=QString::number(cur->first);
        itm+=": ";
        itm+=QString::number(cur->second);
        ui->listWidget->addItem(itm);

        cur++;
    }
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    QString s;
    switch(event->key())
    {
    case Qt::Key_Up:
    case Qt::Key_W:
        s = "u";
        break;
    case Qt::Key_Down:
    case Qt::Key_S:
        s = "d";
        break;
    case Qt::Key_Left:
    case Qt::Key_A:
        s = "l";
        break;
    case Qt::Key_Right:
    case Qt::Key_D:
        s = "r";
        break;
    default:
        QWidget::keyPressEvent(event);
        return;
    }
    sendEventToServer(s);
}

void MainWindow::sendEventToServer(QString str)
{
    QByteArray arrBlock;
    QDataStream out(&arrBlock,QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_8);
    out << (quint32)0;
    out << str;
    out.device()->seek(0);
    out << quint32(arrBlock.size() - sizeof(quint32));
    client->write(arrBlock);
}

void MainWindow::decode(QString msg)
{
    if(msg.mid(1,4) == "map:")
    {
        getField(msg.mid(5,msg.size()-6));
        game->update(game->boundingRect());
        return;
    }
    if(msg.mid(1,11) == "your snake:")
    {
        getYourSnake(msg.mid(12,msg.size()-13));
        game->update(game->boundingRect());
        return;
    }
    if(msg.mid(1,13) == "other snakes:")
    {
        getOtherSnakes(msg.mid(14,msg.size()-15));
        game->update(game->boundingRect());
        return;
    }
    if(msg.mid(1,10) == "new bonus:")
    {
        getBonus(msg.mid(11,msg.size()-12));
        game->update(game->boundingRect());
        return;
    }
    if(msg.mid(1,10) == "new snake:")
    {
        getNewSnake(msg.mid(11,msg.size()-12));
        game->update(game->boundingRect());
        return;
    }
    if(msg.mid(1,7) == "update:")
    {
        getUpdate(msg.mid(8,msg.size()-9));
        game->update(game->boundingRect());
        return;
    }
    if(msg.mid(1,13) == "Disconnected:")
    {
        getDisconected(msg.mid(14,msg.size()-15));
        game->update(game->boundingRect());
        return;
    }
    if(msg.mid(1,8) == "Bonuses:")
    {
        getExistBonuses(msg.mid(9,msg.size()-10));
        game->update(game->boundingRect());
        return;
    }
}

void MainWindow::getField(QString msg)
{
    Point size;
    QString tempSubStr;
    quint32 poz = msg.indexOf('(');
    quint32 newPoz = msg.indexOf(' ',poz+2);
    tempSubStr = msg.mid(poz+2,newPoz-poz-2);
    size.first = tempSubStr.toInt();
    poz = newPoz;
    newPoz = msg.indexOf(' ',newPoz+1);
    tempSubStr = msg.mid(poz+1,newPoz-poz-1);
    size.second = tempSubStr.toInt();
    game->setFieldSize(size);
    poz = msg.indexOf(')');
    game->setField(msg.mid(poz+1));
}

void MainWindow::getYourSnake(QString msg)
{
    myID = getNewSnake(msg);
    game->setMySnakeID(myID);
    updatePoints();
}

void MainWindow::getOtherSnakes(QString msg)
{
    quint32 poz, newPoz = 0;

    while(true)
    {
        poz = msg.indexOf("ID = ",newPoz);
        if(poz == -1)
            break;
        newPoz = msg.indexOf("ID = ",poz+5);
        if(newPoz == -1)
        {
            getNewSnake(msg.mid(poz));
            break;
        }
        else
            getNewSnake(msg.mid(poz,newPoz));
    }
}

quint32 MainWindow::getNewSnake(QString msg)
{
    QString tempSubStr;
    quint32 poz, newPoz;

    poz = msg.indexOf("ID = ");
    newPoz = msg.indexOf(' ',poz+5);
    tempSubStr = msg.mid(poz+5,newPoz-poz-5);
    quint32 ID = tempSubStr.toInt();

    poz = msg.indexOf("points = ");
    newPoz = msg.indexOf(' ',poz + 9);
    tempSubStr = msg.mid(poz+9,newPoz-poz-9);
    quint32 score = tempSubStr.toInt();


    poz = newPoz;
    newPoz = msg.indexOf(' ', poz+1);
    tempSubStr = msg.mid(poz+1,newPoz-poz-1);
    int N = tempSubStr.toInt();
    QList<Point > body;
    for(int i = 0; i < N; i++)
    {
        Point tempPoint;
        poz = msg.indexOf('(',newPoz);
        newPoz = msg.indexOf(' ',poz);
        tempSubStr = msg.mid(poz+1,newPoz-poz-1);
        tempPoint.first = tempSubStr.toInt();

        poz = newPoz;
        newPoz = msg.indexOf(')',poz);
        tempSubStr = msg.mid(poz+1,newPoz-poz-1);
        tempPoint.second = tempSubStr.toInt();

        body.push_back(tempPoint);
    }

    game->addSnake(ID, body);
    game->addPointToPlayer(ID,score);

    updatePoints();
    return ID;
}

void MainWindow::getUpdate(QString msg)
{
    QString tempSubStr;
    quint32 poz, newPoz;
    newPoz = 0;
    while(newPoz < msg.size()-1) // ...} ]
    {
        poz = msg.indexOf('{', newPoz);
        newPoz = msg.indexOf(' ', poz+2);
        tempSubStr = msg.mid(poz+2, newPoz-poz-2);
        quint32 ID = tempSubStr.toInt();


        char type = msg.at(newPoz+1).toAscii();


        if(type == 't')
        {
            char direction = msg.at(newPoz+3).toAscii();
            game->moveSnake(ID, direction);
            newPoz = msg.indexOf('}',poz+1);
            newPoz++;
            continue;
        }
        if(type == 'd')
        {

            poz = newPoz+2;
            newPoz = msg.indexOf(' ', poz+1);
            tempSubStr = msg.mid(poz+1,newPoz-poz-1);
            int N = tempSubStr.toInt();
            QList<Point > body;
            for(int i = 0; i < N; i++)
            {
                Point tempPoint;
                poz = msg.indexOf('(',newPoz);
                newPoz = msg.indexOf(' ',poz);
                tempSubStr = msg.mid(poz+1,newPoz-poz-1);
                tempPoint.first = tempSubStr.toInt();

                poz = newPoz;
                newPoz = msg.indexOf(')',poz);
                tempSubStr = msg.mid(poz+1,newPoz-poz-1);
                tempPoint.second = tempSubStr.toInt();

                body.push_back(tempPoint);
            }


            game->killSnakeAndRespawn(ID,body);
            newPoz = msg.indexOf('}', poz+1);
            newPoz++;
            continue;
        }
        if(type == 'e')
        {
            char direction = msg.at(newPoz+3).toAscii();
            game->moveSnake(ID, direction);
            //TODO: diferent type bonus
            game->addPointToPlayer(ID,BONUS_SIMPLE_PRIZE);
            updatePoints();

            poz = newPoz + 5;
            if(msg.at(poz).toAscii() == '+')
            {
                newPoz = msg.indexOf(' ',poz);
                tempSubStr = msg.mid(poz,newPoz - poz);
                quint32 resize = tempSubStr.toInt();
                for(int i = 0; i < resize; i++)
                    game->increaseSnake(ID);
            }
            poz = newPoz;
            newPoz = msg.indexOf(')',poz);
            newPoz++;
            getBonus(msg.mid(poz,newPoz));
            newPoz = msg.indexOf('}',poz);
            newPoz++;
        }
    }
    game->update(game->boundingRect());
}

void MainWindow::getDisconected(QString msg)
{
    QString tempSubStr;
    quint32 poz, newPoz;
    poz = msg.indexOf(' ');
    newPoz = msg.indexOf(' ',poz+1);
    tempSubStr = msg.mid(poz+1,newPoz-poz-1);
    quint32 ID = tempSubStr.toInt();
    game->deleteSnake(ID);
    game->deleteBonus(ID);

    updatePoints();
}

void MainWindow::getBonus(QString msg)
{
    QString tempSubStr;
    quint32 poz, newPoz;
    poz = msg.indexOf("ID =");
    poz = msg.indexOf(' ',poz+3);
    newPoz = msg.indexOf(' ',poz+1);
    tempSubStr = msg.mid(poz+1,newPoz - poz -1);
    quint32 ID = tempSubStr.toInt();

    poz = newPoz;
    newPoz = msg.indexOf(' ',poz+1);
    tempSubStr = msg.mid(poz+1,newPoz - poz -1);
    quint32 type = tempSubStr.toInt();

    Point pos;
    newPoz = msg.indexOf('(');
    poz = msg.indexOf(' ',newPoz+1);
    newPoz = msg.indexOf(' ',poz+1);
    tempSubStr = msg.mid(poz+1,newPoz - poz -1);
    pos.first = tempSubStr.toInt();

    poz = newPoz;
    newPoz = msg.indexOf(' ',poz+1);
    tempSubStr = msg.mid(poz+1,newPoz - poz -1);
    pos.second = tempSubStr.toInt();

    game->addBonus(ID,pos,type);
}

void MainWindow::getExistBonuses(QString msg)
{
    quint32 poz, newPoz = 0;

    while(true)
    {
        poz = msg.indexOf("ID = ",newPoz);
        if(poz == -1)
            break;
        newPoz = msg.indexOf("ID = ",poz+5);
        if(newPoz == -1)
        {
            getBonus(msg.mid(poz));
            break;
        }
        else
            getBonus(msg.mid(poz,newPoz));
    }
}

void MainWindow::closeEvent(QCloseEvent *ev)
{
    disconnect(client,SIGNAL(readyRead()),this,SLOT(slotReadyRead()));
    client->disconnectFromHost();
    ev->accept();
}

void MainWindow::slotReadyRead()
{
    QDataStream in(client);
    in.setVersion(QDataStream::Qt_4_8);
    QString str;
    for(;;)
    {
        if(!blockSize)
            if(client->bytesAvailable() < sizeof(quint32))
                break;
            else
                in >>blockSize;
        if(client->bytesAvailable() < blockSize)
            break;
        str.resize(blockSize);
        blockSize = 0;
        in >> str;
        decode(str);
    }
}

void MainWindow::on_pushButton_clicked()
{
    QHostAddress myIP("89.252.76.175");
    client->connectToHost(myIP,1234);
    connect(client,SIGNAL(readyRead()),this,SLOT(slotReadyRead()));
}

void MainWindow::on_pushButton_2_clicked()
{
    disconnect(client,SIGNAL(readyRead()),this,SLOT(slotReadyRead()));
    client->disconnectFromHost();
}
